/**
 *  This file is part of PennerBot
 *
 *  Copyright (C) 2010 microkernel
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <cmath>

#include <core/convert.hpp>
#include <core/messages.hpp>
#include <core/portable.hpp>
#include <core/regex.hpp>

#include <core/modules/bottlesell.hpp>


namespace PBot
{
namespace Core
{

BottleSell::BottleSell(Connection& _con, Config* _cfg)
 : Module(_con, _cfg), paused(false), oldExchangeRate(-1.0), failedAttempts(0)
{
    moduleName = "BottleSell";
}

BottleSell::~BottleSell()
{
}

bool BottleSell::Do()
{
    if(!cfg->BS_enabled && !paused)
    {
        listener.Dispatch(cfg->id, MSG_BS_DISABLED);
        paused = true;
        return true;
    }
    else if(!cfg->BS_enabled && paused)
    {
        return true;
    }
    else if(cfg->BS_enabled && paused)
    {
        listener.Dispatch(cfg->id, MSG_BS_ENABLED);
        paused = false;
    }

    while(failedAttempts < MAX_BS_ATTEMPTS)
    {
        // Load overview page
        std::string url = cfg->website + "/overview/";
        if(!con.Get(url))
        {
            ++failedAttempts;
            continue;
        }

        PortableSleep(1,3);

        // Get container size
        std::string containerSizeStr;

        if(!str.GetRegex(cfg->game, Strings::BS_CONTAINER)->Search(con.GetBuffer(), containerSizeStr))
        {
            listener.Dispatch(cfg->id, MSG_BS_CONTAINERSIZEFAIL);
            ++failedAttempts;
            continue;
        }

        RemoveDelimiters(containerSizeStr, cfg->americanFormat);
        double containerSize = StrToVar<double>(containerSizeStr);


        // Get amount of money
        std::string moneyStr;

        if(!str.GetRegex(cfg->game, Strings::BS_MONEY)->Search(con.GetBuffer(), moneyStr))
        {
            listener.Dispatch(cfg->id, MSG_BS_MONEYFAIL);
            ++failedAttempts;
            continue;
        }

        RemoveDelimiters(moneyStr, cfg->americanFormat);
        double money = StrToVar<double>(moneyStr);

        // Load bottle page
        url = cfg->website + "/stock/";
        if(!con.Get(url))
        {
            ++failedAttempts;
            continue;
        }

        PortableSleep(1,3);

        url = cfg->website + "/stock/bottle/";
        if(!con.Get(url))
        {
            ++failedAttempts;
            continue;
        }

        PortableSleep(1,3);

        // Get exchange rate
        std::string exchangeRateStr;

        if(!str.GetRegex(cfg->game, Strings::BS_EXCHANGERATE)->Search(con.GetBuffer(), exchangeRateStr))
        {
            listener.Dispatch(cfg->id, MSG_BS_EXCHANGERATEFAIL);
            ++failedAttempts;
            continue;
        }

        RemoveDelimiters(exchangeRateStr, cfg->americanFormat);
        double exchangeRate = StrToVar<double>(exchangeRateStr);

        // Get exchange rate
        unsigned int bottleCount;

        if(!str.GetRegex(cfg->game, Strings::BS_BOTTLECOUNT)->Search(con.GetBuffer(), bottleCount))
        {
            listener.Dispatch(cfg->id, MSG_BS_BOTTLECOUNTFAIL);
            ++failedAttempts;
            continue;
        }

        // Get action value
        std::string action;
        if(!str.GetRegex(cfg->game, Strings::BS_ACTION)->Search(con.GetBuffer(), action))
        {
            listener.Dispatch(cfg->id, MSG_BS_ACTIONFAIL);
            ++failedAttempts;
            continue;
        }

        // Get chkval
        std::string chkval;
        if(!str.GetRegex(cfg->game, Strings::BS_CHKVAL)->Search(con.GetBuffer(), chkval))
        {
            listener.Dispatch(cfg->id, MSG_BS_CHKVALFAIL);
            ++failedAttempts;
            continue;
        }

        // Check formular
        if(con.GetBuffer().find(str.GetString(cfg->game, Strings::BS_MAX)) == std::string::npos
                || con.GetBuffer().find(str.GetString(cfg->game, Strings::BS_SUM)) == std::string::npos
                || con.GetBuffer().find(str.GetString(cfg->game, Strings::BS_BUTTON)) == std::string::npos)
        {
            listener.Dispatch(cfg->id, MSG_BS_FORMFAIL);
            ++failedAttempts;
            continue;
        }

        if(oldExchangeRate != exchangeRate)
        {
            oldExchangeRate = exchangeRate;
            listener.Dispatch(cfg->id, MSG_BS_RATECHANGED, exchangeRateStr);
        }

        if(exchangeRate >= cfg->BS_minExchange)
        {
            if(bottleCount == 0)
            {
                listener.Dispatch(cfg->id, MSG_BS_NOBOTTLES);
            }
            else
            {
                double target;

                if(cfg->BS_maxMoney == 0)
                {
                    target = containerSize - money;
                }
                else
                {
                    target = cfg->BS_maxMoney - money;
                    if(target > containerSize)
                    {
                        target = containerSize - money;
                    }
                }

                unsigned int neededBottles = std::floor(target/exchangeRate);

                if(target > exchangeRate)
                {
                    if(neededBottles > bottleCount)
                    {
                        neededBottles = bottleCount;
                    }

                    PortableSleep(5,7);

                    std::string postdata = "chkval=" + chkval + "&max=" + NumToStr(bottleCount) + "&sum=" + NumToStr(neededBottles);

                    if(!con.Post(action, postdata))
                    {
                        ++failedAttempts;
                        continue;
                    }

                    listener.Dispatch(cfg->id, MSG_BS_SOLDBOTTLES, NumToStr(neededBottles*exchangeRate));
                }
                else
                {
                    listener.Dispatch(cfg->id, MSG_BS_MAXREACHED);
                }
            }
        }

        Sleep(30, 120);
        return true;

    }

    listener.Dispatch(cfg->id, MSG_BS_TOOMANYATTEMPTS);
    failedAttempts = 0;

    Sleep(10*60);

    return true;
}


}
}
